/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
 * Description: read data by byte in ff
 * Author: HiMobileCam middleware develop team
 * Create: 2017-12-22
 */

#include "ffbitsreader.h"

#include <stdlib.h>
#include "../ffdemux_log.h"

HI_S32 FFBitsReaderCreate(FfBitReader **outReader, const HI_U8 *data, HI_U32 dataSize)
{
    if (outReader == HI_NULL || data == HI_NULL || dataSize == 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FFBitsReaderCreate fail illegal param\n");
        return HI_FAILURE;
    }
    FfBitReader *reader  = (FfBitReader *)malloc(sizeof(FfBitReader));
    if (reader == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FFBitsReaderCreate malloc fail\n");
        return HI_FAILURE;
    }

    reader->data = data;
    reader->size = dataSize;
    reader->offset = 0;

    *outReader = reader;

    return HI_SUCCESS;
}

HI_S32 FFBitsReaderDestroy(FfBitReader *reader)
{
    if (reader == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FFBitsReaderDestroy fail : param is null\n");
        return HI_FAILURE;
    }
    free(reader);

    return HI_SUCCESS;
}

static HI_U8 BitReadUV(FfBitReader *reader, HI_U8 bits)
{
    HI_U8 val = 0;
    for (HI_S32 i = 0; i < bits; i++) {
        if (FfBitReadU1(reader)) {
            val += 1 << (bits - i - 1);
        }
    }
    return val;
}

HI_BOOL FfBitReadU1(FfBitReader *reader)
{
    if (reader == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FfBitReadU1 null param \n");
        return HI_FAILURE;
    }
    if (reader->data == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FfBitReadU1 reader->data null param \n");
        return HI_FAILURE;
    }

    if (reader->offset < reader->size * 8) {
        HI_U32 offset = reader->offset++;
        if (((reader->data[offset / 8] >> (8 - offset % 8 - 1)) & 1) == 1) {
            return HI_TRUE;
        } else {
            return HI_FALSE;
        }
    } else {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "OutOfRange Size %d Offset: %d \n", reader->size, reader->offset);
        return HI_FALSE;
    }
}

HI_U32 FfBitReadUN(FfBitReader *reader, HI_U8 bits)
{
    HI_U32 val = 0;
    for (HI_S32 i = 0; i < bits; i++) {
        if (FfBitReadU1(reader)) {
            val += 1 << (bits - i - 1);
        }
    }
    return val;
}

HI_U8 FfBitReadU2(FfBitReader *reader)
{
    return BitReadUV(reader, 2);
}

HI_U8 FfBitReadU3(FfBitReader *reader)
{
    return BitReadUV(reader, 3);
}

HI_U8 FfBitReadU4(FfBitReader *reader)
{
    return BitReadUV(reader, 4);
}

HI_U8 FfBitReadU5(FfBitReader *reader)
{
    return BitReadUV(reader, 5);
}

HI_U8 FfBitReadU6(FfBitReader *reader)
{
    return BitReadUV(reader, 6);
}

HI_U8 FfBitReadU8(FfBitReader *reader)
{
    return BitReadUV(reader, 8);
}

HI_U16 FfBitReadU16(FfBitReader *reader)
{
    HI_U16 val = FfBitReadU8(reader) << 8;
    val += FfBitReadU8(reader);
    return val;
}

HI_U32 FfBitReadU32(FfBitReader *reader)
{
    HI_U32 val = FfBitReadU8(reader) << 24;
    val += FfBitReadU8(reader) << 16;
    val += FfBitReadU8(reader) << 8;
    val += FfBitReadU8(reader);
    return val;
}

HI_U32 FfBitReadUeV(FfBitReader *reader)
{
    HI_U32 suffix = 0;
    HI_U32 numZeros = 0;
    HI_U32 tryTimes = 33;

    if (reader == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "FfBitReadUeV  null param \n");
        return 0;
    }

    while (!FfBitReadU1(reader) && reader->offset < reader->size * 8 && tryTimes > 0) {
        numZeros++;
        tryTimes--;
    }

    for (HI_U32 i = 0; i < numZeros; i++) {
        if (FfBitReadU1(reader)) {
            suffix += 1 << (numZeros - i - 1);
        }
    }
    HI_U32 codeNum = (1 << numZeros) + suffix - 1;
    return codeNum;
}

HI_S32 FfBitReadSeV(FfBitReader *reader)
{
    HI_S32 val = 0;
    HI_U32 codeNum = FfBitReadUeV(reader);
    if (codeNum % 2) {
        val = -1 * (codeNum / 2);
    } else {
        val = codeNum / 2;
    }
    return val;
}
